/*
 * ResultGenerator.cpp
 *
 *      Author: Jerome RD Soine and Christoph A Brand
 *      Institution: Schwarz goup, Institute for Theoretical Physics, Heidelberg University, Germany
 */

#include "ResultGenerator.h"
#include <fstream>
#include "include/core/utility/VectorOperations.h"
#include "include/base/cell_models/ACM/ActiveCableModel.h"
#include "include/base/substrate_models/FEM/FEMCalc.h"
#include "include/core/comparator/L2Comparator.h"
#include "OptimizationObject.h"
#include "FADirections.h"

vector<double> ResultGenerator::read_solution_file(string filename){
	vector<double> sol;
	ifstream fin(filename.c_str());
	string line;
	while (getline(fin, line)) {
		sol.push_back(atof(line.c_str()));
	}
	fin.close();
	return sol;
}

double ResultGenerator::getL2(string parameter_file, string solution_file){
	OptimizationObject* opti_object = new OptimizationObject(parameter_file);

	vector<double> sol = read_solution_file(solution_file);
	opti_object->initializeWorkerManager("","",sol);
	WorkerManager* manager = WorkerManager::getInstance();
	Worker* w = manager->getWorker(sol,-1);
	w->work();
	return w->get_fitness();
}

void ResultGenerator::compareL2(string parameter_file, string solution_file1, string solution_file2){
	OptimizationObject* opti_object = new OptimizationObject(parameter_file);
	vector<double> sol1 = read_solution_file(solution_file1), sol2 = read_solution_file(solution_file2);

	opti_object->initializeWorkerManager("","",sol1);
	WorkerManager* manager = WorkerManager::getInstance();
	Worker* w1 = manager->getWorker(sol1,-1);
	Worker* w2 = manager->getWorker(sol2,-1);
	w1->start();
	w2->start();
	w2->join();
	w1->join();
	cout << "First L2: " << w1->get_fitness() << " - Second L2: " << w2->get_fitness() << endl;
}

void ResultGenerator::get_forces(string parameter_file, string solution_file)
{
	OptimizationObject* opti_object = new OptimizationObject(parameter_file);
	vector<double> sol = read_solution_file(solution_file);
	// First component with SFs, second without.
	vector<double> traction_sum (2);
	opti_object->initializeWorkerManager("","",sol);
	WorkerManager* worker_manager = WorkerManager::getInstance();

	// Calculate traction for full tension
	Worker* worker1 = worker_manager->getWorker(sol,-1);
	worker1->start();
	// Calculate traction only with network
	for(unsigned int i=1;i<sol.size();i++) sol[i] = 0;
	Worker* worker2 = worker_manager->getWorker(sol,-1);
	worker2->start();

	worker2->join();
	worker1->join();

	vector<ForceTransmission*> ft1 = worker1->get_cell_model()->get_force_transmissions();
	vector<ForceTransmission*> ft2 = worker2->get_cell_model()->get_force_transmissions();
	for(unsigned int i=0; i<ft1.size(); i++){
		traction_sum [0] += ft1[i]->get_force_magnitude();
		traction_sum [1] += ft2[i]->get_force_magnitude();
	}

	worker1->unlock();
	worker2->unlock();

	cout << "F_tot = " << traction_sum[0] << "\n";
	cout << "F_NT = " << traction_sum[1] << "\n";
}

void ResultGenerator::execute_sampling_around_point(string output_path, string parameter_file, string solution_file, unsigned int x_tension_number, unsigned int y_tension_number,unsigned int step_number, double x_width, double y_width)
{
	OptimizationObject* opti_object = new OptimizationObject(parameter_file);

	unsigned int N_THREADS = opti_object->get_n_threads();

	vector<double> sol = read_solution_file(solution_file);
	vector<double> tmp_sol = sol;
	opti_object->initializeWorkerManager("","",sol);
	WorkerManager* worker_manager = WorkerManager::getInstance();

	 Worker * preWorker = worker_manager->getWorker(sol,-1);
	 preWorker->work();
	 vector<double> init = preWorker->get_final_configuration();
     if(step_number%2==0) step_number++;
	 vector<Worker*> workers (step_number*step_number);
	 double init_L2 = preWorker->get_fitness();

	 for(unsigned int i=0;i<step_number;i++)
		 for(unsigned int j=0;j<step_number;j++)
		 {
			 tmp_sol = sol;
			 tmp_sol[x_tension_number] = tmp_sol[x_tension_number] -(step_number-1)/2*x_width+i*x_width;
			 tmp_sol[y_tension_number] = tmp_sol[y_tension_number] -(step_number-1)/2*y_width+j*y_width;
			 workers[i*step_number+j] = worker_manager->getWorker(tmp_sol,-1);
		 }

	 if(step_number*step_number <= N_THREADS){
	     	for(unsigned int i=0; i<step_number*step_number; i++){
	     		workers[i]->start();
	     	}
	     	for(unsigned int i=0; i<step_number*step_number; i++){
	     		workers[i]->join();
	     	}
	     } else {
	     	vector<int> worker_numbers(N_THREADS, -1);
	     	for(unsigned int i=0; i<N_THREADS; i++){
	     		worker_numbers[i] = i;
	     		workers[i]->start();
	     	}
	     	unsigned int next_worker_number = N_THREADS;
	     	while(true){
	     		for(unsigned int i=0; i<N_THREADS; i++){
	     			if(worker_numbers[i] >= 0){
	     				bool joined = workers[worker_numbers[i]]->try_join_for(10l);
	     				if(joined){
	     					//replace job
	     					if(next_worker_number < step_number*step_number){
	     						worker_numbers[i] = next_worker_number++;
	     						workers[worker_numbers[i]]->start();
	     					} else {
	     						worker_numbers[i] = -1;
	     					}
	     				}
	     			}
	     		}
	     		bool all_jobs_done = true;
	     		for(unsigned int i=0; i<N_THREADS; i++){
	     			if(worker_numbers[i] >= 0) all_jobs_done = false;
	     		}
	     		if(all_jobs_done) break;
	     	}

	     }

	 stringstream ss1; ss1 << x_tension_number;
	 stringstream ss2; ss2 << y_tension_number;

	 ofstream data ((output_path + "sample_" + ss1.str() + "_" + ss2.str() + ".txt").c_str());
	 for(unsigned int i=0;i<step_number;i++)
	 		 for(unsigned int j=0;j<step_number;j++)
	 		 {
	 			 tmp_sol = sol;
	 			 data << tmp_sol[x_tension_number] -(step_number-1)/2*x_width+i*x_width << "\t";
	 			 data << tmp_sol[y_tension_number] -(step_number-1)/2*y_width+j*y_width << "\t";
	 			 data << workers[i*step_number+j]->get_fitness()/init_L2 << "\n";
	 		 }
	 data.close();
}


